package top.zhouze.common.util;

import top.zhouze.common.annotation.MmkvField;
import com.google.gson.Gson;
import com.tencent.mmkv.MMKV;
import lombok.SneakyThrows;

/**
 * @author 周仄
 * @since 2023/2/1 15:38
 */
public class MmkvUtil {

    /**
     * 将对象的属性展开存储到本地
     *
     * @param obj obj
     * @throws IllegalAccessException 非法访问异常
     */
    @SneakyThrows
    public static void putObjFlat(Object obj) {
        MMKV mv = MMKV.defaultMMKV();
        Class<?> clazz = obj.getClass();
        MmkvField kvCls = clazz.getAnnotation(MmkvField.class);
        String kvPrefix;
        if (kvCls == null || kvCls.value().isEmpty()) {
            kvPrefix = clazz.getSimpleName();
        }else {
            kvPrefix = kvCls.value();
        }

        var fields = clazz.getDeclaredFields();
        for (var field : fields){
            field.setAccessible(true);
            String key = field.getName();
            Object object = field.get(obj);
            if (object == null) {
                continue;
            }
            MmkvField mmKvField = field.getAnnotation(MmkvField.class);
            if (mmKvField != null) {
                if (mmKvField.ignore()) {
                    continue;
                }
                if (!mmKvField.value().isEmpty()){
                    key = mmKvField.value();
                }
            }
            key = kvPrefix+":"+key;

            if (object instanceof String) {
                mv.encode(key, (String) object);
            } else if (object instanceof Integer) {
                mv.encode(key, (Integer) object);
            } else if (object instanceof Boolean) {
                mv.encode(key, (Boolean) object);
            } else if (object instanceof Float) {
                mv.encode(key, (Float) object);
            } else if (object instanceof Long) {
                mv.encode(key, (Long) object);
            } else if (object instanceof Double) {
                mv.encode(key, (Double) object);
            } else if (object instanceof byte[] ) {
                mv.encode(key, (byte[]) object);
            } else {
                Gson gson = new Gson();
                mv.encode(key, gson.toJson(object));
            }
        }

    }

    @SneakyThrows
    public static <T>T getObjFlat(Class<T> clazz) {
        MMKV mv = MMKV.defaultMMKV();
        T result = clazz.getDeclaredConstructor().newInstance();
        String kvPrefix;
        MmkvField kvCls = clazz.getAnnotation(MmkvField.class);
        if (kvCls == null || kvCls.value().isEmpty()) {
            kvPrefix = clazz.getSimpleName();
        }else {
            kvPrefix = kvCls.value();
        }
        var fields = clazz.getDeclaredFields();
        for (var field : fields){
            field.setAccessible(true);
            String key = field.getName();

            MmkvField mmKvField = field.getAnnotation(MmkvField.class);
            if (mmKvField != null) {
                if (mmKvField.ignore()) {
                    continue;
                }
                if (!mmKvField.value().isEmpty()){
                    key = mmKvField.value();
                }
            }
            key = kvPrefix+":"+key;
            var cls = field.getType();
            if (String.class.equals(cls)) {
                field.set(result,mv.decodeString(key));
            } else if (Integer.class.equals(cls)) {
                field.set(result,mv.decodeInt(key));
            } else if (Boolean.class.equals(cls)) {
                field.set(result,mv.decodeBool(key));
            } else if (Float.class.equals(cls)) {
                field.set(result,mv.decodeFloat(key));
            } else if (Long.class.equals(cls)) {
                field.set(result,mv.decodeLong(key));
            } else if (Double.class.equals(cls)) {
                field.set(result,mv.decodeDouble(key));
            } else if (byte[].class.equals(cls)) {
                field.set(result,mv.decodeBytes(key));
            } else {
                Gson gson = new Gson();
                field.set(result,gson.fromJson(mv.decodeString(key),cls));
            }
        }
        return result;
    }
}
